home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / cvs / sprite / commit.c < prev    next >
C/C++ Source or Header  |  1991-10-08  |  22KB  |  764 lines

  1. #ifndef lint
  2. static char rcsid[] = "$Id: commit.c,v 1.4 91/09/10 16:11:21 jhh Exp $";
  3. #endif !lint
  4.  
  5. /*
  6.  *    Copyright (c) 1989, Brian Berliner
  7.  *
  8.  *    You may distribute under the terms of the GNU General Public License
  9.  *    as specified in the README file that comes with the CVS 1.0 kit.
  10.  *
  11.  * Commit Files
  12.  *
  13.  *    "commit" commits the present version to the RCS repository, AFTER
  14.  *    having done a test on conflicts.  The call is:
  15.  *        cvs commit [options] files...
  16.  *
  17.  *    "commit" accepts the following options:
  18.  *        -f        Force a commit, even if the RCS $Id string
  19.  *                is not found
  20.  *        -n        Causes "commit" to *not* run any commit prog
  21.  *        -a        Commits all files in the current directory
  22.  *                that have been modified.
  23.  *        -m 'message'    Does not start up the editor for the
  24.  *                log message; just gleans it from the
  25.  *                'message' argument.
  26.  *        -r Revision    Allows committing to a particular *numeric*
  27.  *                revision number.
  28.  *
  29.  *    Note that "commit" does not do a recursive commit.  You must do
  30.  *    "commit" in each directory where there are files that you'd
  31.  *    like to commit.
  32.  */
  33.  
  34. #include <sys/param.h>
  35. #include <sys/types.h>
  36. #include <sys/stat.h>
  37. #include <ctype.h>
  38. #include "cvs.h"
  39.  
  40. static int force_commit_no_rcsid = 0;
  41.  
  42. extern int run_module_prog;
  43.  
  44. commit(argc, argv)
  45.     int argc;
  46.     char *argv[];
  47. {
  48.     int commit_all = 0, err = 0;
  49.     char *rev = "";            /* not NULL! */
  50.     char line[MAXLINELEN], message[MAXMESGLEN];
  51.     int c;
  52.  
  53.     if (argc == -1)
  54.     commit_usage();
  55.     /*
  56.      * For log purposes, do not allow "root" to commit files
  57.      */
  58.     if (geteuid() == 0)
  59.     error(0, "cannot commit files as 'root'");
  60.     optind = 1;
  61.     while ((c = getopt(argc, argv, "fnam:r:qQ")) != -1) {
  62.     switch (c) {
  63.     case 'f':
  64.         force_commit_no_rcsid = 1;
  65.         break;
  66.     case 'n':
  67.         run_module_prog = 0;
  68.         break;
  69.     case 'a':
  70.         commit_all = 1;
  71.         break;
  72.     case 'm':
  73.         use_editor = FALSE;
  74.         if (strlen(optarg) >= sizeof(message)) {
  75.         warn(0, "warning: message too long; truncated!");
  76.         (void) strncpy(message, optarg, sizeof(message));
  77.         message[sizeof(message) - 1] = '\0';
  78.         } else
  79.         (void) strcpy(message, optarg);
  80.         break;
  81.     case 'r':
  82.         if (!isdigit(optarg[0]))
  83.         error(0, "specified revision %s must be numeric!", optarg);
  84.         rev = optarg;
  85.         break;
  86.     case 'Q':
  87.         really_quiet = 1;
  88.         /* FALL THROUGH */
  89.     case 'q':
  90.         quiet = 1;
  91.         break;
  92.     case '?':
  93.     default:
  94.         commit_usage();
  95.         break;
  96.     }
  97.     }
  98.     argc -= optind;
  99.     argv += optind;
  100.     if (!commit_all && argc == 0)
  101.     error(0, "must specify the files you'd like to check-in");
  102.     if (commit_all && argc != 0)
  103.     error(0, "cannot specify files with the -a option");
  104.     Name_Repository();
  105.     Writer_Lock();
  106.     if (commit_all) {
  107.     Find_Names(&fileargc, fileargv, ALL);
  108.     argc = fileargc;
  109.     argv = fileargv;
  110.     }
  111.     if (rev[0] != '\0') {
  112.     register int i;
  113.     FILE *fptty;
  114.  
  115. #ifdef sprite
  116.     fptty = open_file(getenv("TTY"), "r");
  117. #else
  118.     fptty = open_file("/dev/tty", "r");
  119. #endif
  120.     printf("WARNING:\n");
  121.     printf("\tCommitting with a specific revision number\n");
  122.     printf("\tbypasses all consistency checks.  Are you abosulutely\n");
  123.     printf("\tsure you want to continue (y/n) [n] ? ");
  124.     (void) fflush(stdout);
  125.     if (fgets(line, sizeof(line), fptty) == NULL ||
  126.         (line[0] != 'y' && line[0] != 'Y')) {
  127.         error(0, "commit of revision %s aborted", rev);
  128.     }
  129.     (void) fclose(fptty);
  130.     /*
  131.      * When committing with a specific revision number, we simply
  132.      * fudge the lists that Collect_Sets() would have created for
  133.      * us.  This is all so gross, but sometimes useful.
  134.      */
  135.     Clist[0] = Glist[0] = Mlist[0] = Olist[0] = Dlist[0] = '\0';
  136.     Alist[0] = Rlist[0] = Wlist[0] = Llist[0] = Blist[0] = '\0';
  137.     for (i = 0; i < argc; i++) {
  138.         (void) strcat(Mlist, " ");
  139.         (void) strcat(Mlist, argv[i]);
  140.     }
  141.     } else {
  142.     err += Collect_Sets(argc, argv);
  143.     }
  144.     if (err == 0) {
  145.     err += commit_process_lists(message, rev);
  146.     if (err == 0 && run_module_prog) {
  147.         char *cp;
  148.         FILE *fp;
  149.  
  150.         /*
  151.          * It is not an error if Checkin.prog does not exist.
  152.          */
  153.         if ((fp = fopen(CVSADM_CIPROG, "r")) != NULL) {
  154.         if (fgets(line, sizeof(line), fp) != NULL) {
  155.             if ((cp = rindex(line, '\n')) != NULL)
  156.             *cp = '\0';
  157.             (void) sprintf(prog, "%s %s", line, Repository);
  158.             printf("%s %s: Executing '%s'\n", progname, command, prog);
  159.             (void) system(prog);
  160.         }
  161.         (void) fclose(fp);
  162.         }
  163.     }
  164.     Update_Logfile(Repository, message);
  165.     }
  166.     Lock_Cleanup(0);
  167.     exit(err);
  168. }
  169.  
  170. /*
  171.  * Process all the lists, returning the number of errors found.
  172.  */
  173. static
  174. commit_process_lists(message, rev)
  175.     char *message;
  176.     char *rev;
  177. {
  178.     char line[MAXLISTLEN], fname[MAXPATHLEN], revision[50];
  179.     FILE *fp;
  180.     char *cp;
  181.     int first, err = 0;
  182.  
  183.     /*
  184.      * Doesn't make much sense to commit a directory...
  185.      */
  186.     if (Dlist[0])
  187.     warn(0, "committing directories ignored -%s", Dlist);
  188.     /*
  189.      * Is everything up-to-date?
  190.      * Only if Glist, Olist, and Wlist are all NULL!
  191.      */
  192.     if (Glist[0] || Olist[0] || Wlist[0]) {
  193.     (void) fprintf(stderr, "%s: the following files are not ", progname);
  194.     (void) fprintf(stderr,
  195.                "up to date; use '%s update' first:\n", progname);
  196.     if (Glist[0] != '\0')
  197.         (void) fprintf(stderr, "\t%s\n", Glist);
  198.     if (Olist[0] != '\0')
  199.         (void) fprintf(stderr, "\t%s\n", Olist);
  200.     if (Wlist[0] != '\0')
  201.         (void) fprintf(stderr, "\t%s\n", Wlist);
  202.     Lock_Cleanup(0);
  203.     exit(1);
  204.     }
  205.     /*
  206.      * Is there anything to do in the first place?
  207.      */
  208.     if (Mlist[0] == '\0' && Rlist[0] == '\0' && Alist[0] == '\0') {
  209.     if (!quiet) {
  210.         error(0, "there is nothing to commit!");
  211.     } else {
  212.         Lock_Cleanup(0);
  213.         exit(1);
  214.     }
  215.     }
  216.     /*
  217.      * First we make sure that the file has an RCS $Id string in it
  218.      * and if it does not, the user is prompted for verification to continue.
  219.      */
  220.     if (force_commit_no_rcsid == 0) {
  221.     (void) strcpy(line, Mlist);
  222.     (void) strcat(line, Alist);
  223.     for (first = 1, cp = strtok(line, " \t"); cp;
  224.         cp = strtok((char *)NULL, " \t")) {
  225.         (void) sprintf(prog, "%s -s %s %s", GREP, RCSID_PAT, cp);
  226.         if (system(prog) != 0) {
  227.         if (first) {
  228.             printf("%s %s: WARNING!\n", progname, command);
  229. #ifndef sprite
  230.             printf("\tThe following file(s) do not contain an RCS $Id keyword:\n");
  231. #else
  232.             printf(
  233.     "\tThe following file(s) do not contain an RCS $Id or $Header keyword:\n");
  234. #endif
  235.             first = 0;
  236.         }
  237.         printf("\t\t%s\n", cp);
  238.         }
  239.     }
  240.     if (first == 0) {
  241. #ifdef sprite
  242.         FILE *fptty = open_file(getenv("TTY"), "r");
  243. #else
  244.         FILE *fptty = open_file("/dev/tty", "r");
  245. #endif
  246.         printf("\tAre you sure you want to continue (y/n) [n] ? ");
  247.         (void) fflush(stdout);
  248.         if (fgets(line, sizeof(line), fptty) == NULL ||
  249.         (line[0] != 'y' && line[0] != 'Y')) {
  250.         error(0, "commit aborted");
  251.         }
  252.         (void) fclose(fptty);
  253.     }
  254.     }
  255.     if (use_editor)
  256.     do_editor(message);
  257.     /*
  258.      * Mlist is the "modified, needs committing" list
  259.      */
  260.     (void) strcpy(line, Mlist);
  261.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  262.     (void) strcpy(User, cp);
  263.     (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  264.     if (lock_RCS(rev) != 0)
  265.         err++;
  266.     }
  267.     /*
  268.      * Rlist is the "to be removed" list
  269.      */
  270.     (void) strcpy(line, Rlist);
  271.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  272.     (void) strcpy(User, cp);
  273.     (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  274.     if (lock_RCS(rev) != 0)
  275.         err++;
  276.     }
  277.     /*
  278.      * Alist is the "to be added" list
  279.      */
  280.     (void) strcpy(line, Alist);
  281.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  282.     (void) strcpy(User, cp);
  283.     (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  284.     (void) sprintf(prog, "%s/%s -i -t%s/%s%s", Rcsbin, RCS, CVSADM,
  285.                User, CVSEXT_LOG);
  286.     (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_OPT);
  287.     fp = open_file(fname, "r");
  288.     while (fgets(fname, sizeof(fname), fp) != NULL) {
  289.         if ((cp = rindex(fname, '\n')) != NULL)
  290.         *cp = '\0';
  291.         (void) strcat(prog, " ");
  292.         (void) strcat(prog, fname);
  293.     }
  294.     (void) fclose(fp);
  295.     (void) strcat(prog, " ");
  296.     (void) strcat(prog, Rcs);
  297.     if (system(prog) == 0) {
  298.         fix_rcs_modes(Rcs, User);
  299.     } else {
  300.         warn(0, "could not create %s", Rcs);
  301.         err++;
  302.     }
  303.     }
  304.     /*
  305.      * If something failed, release all locks and restore the default
  306.      * branches
  307.      */
  308.     if (err) {
  309.     int didllist = 0;
  310.     char *branch;
  311.  
  312.     for (cp = strtok(Llist, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  313.         didllist = 1;
  314.         (void) strcpy(User, cp);
  315.         (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  316.         (void) sprintf(prog, "%s/%s -q -u %s", Rcsbin, RCS, Rcs);
  317.         if (system(prog) != 0)
  318.         warn(0, "could not UNlock %s", Rcs);
  319.     }
  320.     if (didllist) {
  321.         for (cp=strtok(Blist, " \t"); cp; cp=strtok((char *)NULL, " \t")) {
  322.         if ((branch = rindex(cp, ':')) == NULL)
  323.             continue;
  324.         *branch++ = '\0';
  325.         (void) strcpy(User, cp);
  326.         (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  327.         (void) sprintf(prog, "%s/%s -q -b%s %s", Rcsbin, RCS,
  328.                    branch, Rcs);
  329.         if (system(prog) != 0)
  330.             warn(0, "could not restore branch %s to %s", branch, Rcs);
  331.         }
  332.     }
  333.     for (cp = strtok(Alist, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  334.         (void) strcpy(User, cp);
  335.         (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  336.         (void) unlink(Rcs);
  337.     }
  338.     Lock_Cleanup(0);
  339.     exit(1);
  340.     }
  341.     /*
  342.      * Got them all, now go ahead;
  343.      * First, add the files in the Alist
  344.      */
  345.     if (Alist[0] != '\0') {
  346.     int maxrev, rev;
  347.  
  348.     /* scan the entries file looking for the max revision number */
  349.     fp = open_file(CVSADM_ENT, "r");
  350.     maxrev = 0;
  351.     while (fgets(line, sizeof(line), fp) != NULL) {
  352.         rev = atoi(line);
  353.         if (rev > maxrev)
  354.         maxrev = rev;
  355.     }
  356.     if (maxrev == 0)
  357.         maxrev = 1;
  358.     (void) fclose(fp);
  359.     (void) sprintf(revision, "-r%d", maxrev);
  360.     (void) strcpy(line, Alist);
  361.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  362.         (void) strcpy(User, cp);
  363.         if (Checkin(revision, message) != 0)
  364.         err++;
  365.         (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_OPT);
  366.         (void) unlink(fname);
  367.         (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_LOG);
  368.         (void) unlink(fname);
  369.     }
  370.     }
  371.     /*
  372.      * Everyone else uses the head as it is set in the RCS file,
  373.      * or the revision that was specified on the command line.
  374.      */
  375.     if (rev[0] != '\0')
  376.     (void) sprintf(revision, "-r%s", rev);
  377.     else
  378.     revision[0] = '\0';
  379.     /*
  380.      * Commit the user modified files in Mlist
  381.      */
  382.     (void) strcpy(line, Mlist);
  383.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  384.     (void) strcpy(User, cp);
  385.     if (Checkin(revision, message) != 0)
  386.         err++;
  387.     }
  388.     /*
  389.      * And remove the RCS files in Rlist, by placing it in the Attic
  390.      */
  391.     (void) strcpy(line, Rlist);
  392.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  393.     int omask;
  394.  
  395.     (void) strcpy(User, cp);
  396.     (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  397.     (void) sprintf(fname, "%s/%s", Repository, CVSATTIC);
  398.     omask = umask(2);
  399.     (void) mkdir(fname, 0777);
  400.     (void) umask(omask);
  401.     (void) sprintf(fname, "%s/%s/%s%s", Repository, CVSATTIC,
  402.                User, RCSEXT);
  403.     (void) sprintf(prog, "%s/%s -u -q %s", Rcsbin, RCS, Rcs);
  404.     if ((system(prog) == 0 && rename(Rcs, fname) != -1) ||
  405.         (!isreadable(Rcs) && isreadable(fname)))
  406.         Scratch_Entry(User);
  407.     else
  408.         err++;
  409.     }
  410.     return (err);
  411. }
  412.  
  413. /*
  414.  * Attempt to place a lock on the RCS file; returns 0 if it could and
  415.  * 1 if it couldn't.  If the RCS file currently has a branch as the head,
  416.  * we must move the head back to the trunk before locking the file, and
  417.  * be sure to put the branch back as the head if there are any errors.
  418.  */
  419. static
  420. lock_RCS(rev)
  421.     char *rev;
  422. {
  423.     char branch[50];
  424.     int err = 0;
  425.  
  426.     branch[0] = '\0';
  427.     /*
  428.      * For a specified, numeric revision of the form "1" or "1.1",
  429.      * (or when no revision is specified ""), definitely move the
  430.      * branch to the trunk before locking the RCS file.
  431.      *
  432.      * The assumption is that if there is more than one revision
  433.      * on the trunk, the head points to the trunk, not a branch...
  434.      * and as such, it's not necessary to move the head in this case.
  435.      */
  436.     if (numdots(rev) < 2) {
  437.     branch_number(Rcs, branch);
  438.     if (branch[0] != '\0') {
  439.         (void) sprintf(prog, "%s/%s -q -b %s", Rcsbin, RCS, Rcs);
  440.         if (system(prog) != 0) {
  441.         warn(0, "cannot change branch to default for %s", Rcs);
  442.         return (1);
  443.         }
  444.     }
  445.     (void) sprintf(prog, "%s/%s -q -l %s", Rcsbin, RCS, Rcs);
  446.     err = system(prog);
  447.     } else {
  448.     (void) sprintf(prog, "%s/%s -q -l%s %s 2>%s",
  449.                Rcsbin, RCS, rev, Rcs, DEVNULL);
  450.     (void) system(prog);
  451.     }
  452.     if (err == 0) {
  453.     (void) strcat(Llist, " ");
  454.     (void) strcat(Llist, User);
  455.     (void) strcat(Blist, " ");
  456.     (void) strcat(Blist, User);
  457.     if (branch[0] != '\0') {
  458.         (void) strcat(Blist, ":");
  459.         (void) strcat(Blist, branch);
  460.     }
  461.     return (0);
  462.     }
  463.     if (branch[0] != '\0') {
  464.     (void) sprintf(prog, "%s/%s -q -b%s %s", Rcsbin, RCS, branch, Rcs);
  465.     if (system(prog) != 0)
  466.         warn(0, "cannot restore branch to %s for %s", branch, Rcs);
  467.     }
  468.     return (1);
  469. }
  470.  
  471. /*
  472.  * A special function used only by lock_RCS() to determine if the current
  473.  * head is pointed at a branch.  Returns the result in "branch" as a null
  474.  * string if the trunk is the head, or as the branch number if the branch
  475.  * is the head.
  476.  */
  477. static
  478. branch_number(rcs, branch)
  479.     char *rcs;
  480.     char *branch;
  481. {
  482.     char line[MAXLINELEN];
  483.     FILE *fp;
  484.     char *cp;
  485.  
  486.     branch[0] = '\0';            /* Assume trunk is head */
  487.     fp = open_file(rcs, "r");
  488.     if (fgets(line, sizeof(line), fp) == NULL) {
  489.     (void) fclose(fp);
  490.     return;
  491.     }
  492.     if (fgets(line, sizeof(line), fp) == NULL) {
  493.     (void) fclose(fp);
  494.     return;
  495.     }
  496.     (void) fclose(fp);
  497.     if (strncmp(line, RCSBRANCH, sizeof(RCSBRANCH) - 1) != 0 ||
  498.     !isspace(line[sizeof(RCSBRANCH) - 1]) ||
  499.     (cp = rindex(line, ';')) == NULL)
  500.     return;
  501.     *cp = '\0';                /* strip the ';' */
  502.     if ((cp = rindex(line, ' ')) == NULL &&
  503.     (cp = rindex(line, '\t')) == NULL)
  504.     return;
  505.     cp++;
  506.     if (*cp == NULL)
  507.     return;
  508.     (void) strcpy(branch, cp);
  509. }
  510.  
  511. /*
  512.  * Puts a standard header on the output which is either being prepared for
  513.  * an editor session, or being sent to a logfile program.  The modified, added,
  514.  * and removed files are included (if any) and formatted to look pretty.
  515.  */
  516. static
  517. setup_tmpfile(fp, prefix)
  518.     FILE *fp;
  519.     char *prefix;
  520. {
  521.     if (Mlist[0] != '\0') {
  522.     (void) fprintf(fp, "%sModified Files:\n", prefix);
  523.     fmt(fp, Mlist, prefix);
  524.     }
  525.     if (Alist[0] != '\0') {
  526.     (void) fprintf(fp, "%sAdded Files:\n", prefix);
  527.     fmt(fp, Alist, prefix);
  528.     }
  529.     if (Rlist[0] != '\0') {
  530.     (void) fprintf(fp, "%sRemoved Files:\n", prefix);
  531.     fmt(fp, Rlist, prefix);
  532.     }
  533. }
  534.  
  535. /*
  536.  * Breaks the files list into reasonable sized lines to avoid line
  537.  * wrap...  all in the name of pretty output.
  538.  */
  539. static
  540. fmt(fp, instring, prefix)
  541.     FILE *fp;
  542.     char *instring;
  543.     char *prefix;
  544. {
  545.     char line[MAXLINELEN];
  546.     char *cp;
  547.     int col;
  548.  
  549.     (void) strcpy(line, instring);    /* since strtok() is destructive */
  550.     (void) fprintf(fp, "%s\t", prefix);
  551.     col = 8;                /* assumes that prefix is < 8 chars */
  552.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  553.     if ((col + strlen(cp)) > 70) {
  554.         (void) fprintf(fp, "\n%s\t", prefix);
  555.         col = 8;
  556.     }
  557.     (void) fprintf(fp, "%s ", cp);
  558.     col += strlen(cp) + 1;
  559.     }
  560.     (void) fprintf(fp, "\n%s\n", prefix);
  561. }
  562.  
  563. /*
  564.  * Builds a temporary file using setup_tmpfile() and invokes the user's
  565.  * editor on the file.  The header garbage in the resultant file is then
  566.  * stripped and the log message is stored in the "message" argument.
  567.  */
  568. static
  569. do_editor(message)
  570.     char *message;
  571. {
  572.     FILE *fp;
  573.     char line[MAXLINELEN], fname[MAXPATHLEN];
  574.     int fd;
  575.  
  576.     message[0] = '\0';
  577.     (void) strcpy(fname, CVSTEMP);
  578.     if ((fd = mkstemp(fname)) < 0)
  579.     error(0, "cannot create temporary file %s", fname);
  580.     if ((fp = fdopen(fd, "w+")) == NULL)
  581.     error(0, "cannot create FILE * to %s", fname);
  582.     setup_tmpfile(fp, CVSEDITPREFIX);
  583.     (void) fprintf(fp, "%sEnter Log.  Lines beginning with '%s' are removed automatically\n",
  584.            CVSEDITPREFIX, CVSEDITPREFIX);
  585.     (void) fprintf(fp, "%s----------------------------------------------------------------------\n", CVSEDITPREFIX);
  586.     (void) fclose(fp);
  587.     (void) sprintf(prog, "%s %s", Editor, fname);
  588.     if (system(prog) != 0)
  589.     warn(0, "warning: editor session failed");
  590.     fp = open_file(fname, "r");
  591.     while (fgets(line, sizeof(line), fp) != NULL) {
  592.     if (strncmp(line, CVSEDITPREFIX, sizeof(CVSEDITPREFIX)-1) == 0)
  593.         continue;
  594.     if ((strlen(message) + strlen(line)) >= MAXMESGLEN) {
  595.         warn(0, "warning: log message truncated!");
  596.         break;
  597.     }
  598.     (void) strcat(message, line);
  599.     }
  600.     (void) fclose(fp);
  601.     (void) unlink(fname);
  602. }
  603.  
  604. /*
  605.  * Uses setup_tmpfile() to pass the updated message on directly to
  606.  * any logfile programs that have a regular expression match for the
  607.  * checked in directory in the source repository.  The log information
  608.  * is fed into the specified program as standard input.
  609.  */
  610. Update_Logfile(repository, message)
  611.     char *repository;
  612.     char *message;
  613. {
  614.     FILE *fp_info;
  615.     char logfile[MAXPATHLEN], title[MAXLISTLEN+MAXPATHLEN], line[MAXLINELEN];
  616.     char path[MAXPATHLEN], default_filter[MAXLINELEN];
  617.     char *exp, *filter, *cp, *short_repository;
  618.     int filter_run, line_number;
  619.  
  620.     if (CVSroot == NULL) {
  621.     warn(0, "CVSROOT variable not set; no log message will be sent");
  622.     return;
  623.     }
  624.     (void) sprintf(logfile, "%s/%s", CVSroot, CVSROOTADM_LOGINFO);
  625.     if ((fp_info = fopen(logfile, "r")) == NULL) {
  626.     warn(0, "warning: cannot open %s", logfile);
  627.     return;
  628.     }
  629.     if (CVSroot != NULL)
  630.     (void) sprintf(path, "%s/", CVSroot);
  631.     else
  632.     (void) strcpy(path, REPOS_STRIP);
  633.     if (strncmp(repository, path, strlen(path)) == 0)
  634.     short_repository = repository + strlen(path);
  635.     else
  636.     short_repository = repository;
  637.     (void) sprintf(title, "'%s%s'", short_repository, Llist);
  638.     default_filter[0] = '\0';
  639.     filter_run = line_number = 0;
  640.     while (fgets(line, sizeof(line), fp_info) != NULL) {
  641.     line_number++;
  642.     if (line[0] == '#')
  643.         continue;
  644.     for (cp = line; *cp && isspace(*cp); cp++)
  645.         ;
  646.     if (*cp == '\0')
  647.         continue;            /* blank line */
  648.     for (exp = cp; *cp && !isspace(*cp); cp++)
  649.         ;
  650.     if (*cp != '\0')
  651.         *cp++ = '\0';
  652.     while (*cp && isspace(*cp))
  653.         cp++;
  654.     if (*cp == '\0') {
  655.         warn(0, "syntax error at line %d file %s; ignored",
  656.          line_number, logfile);
  657.         continue;
  658.     }
  659.     filter = cp;
  660.     if ((cp = rindex(filter, '\n')) != NULL)
  661.         *cp = '\0';            /* strip the newline */
  662.     /*
  663.      * At this point, exp points to the regular expression, and
  664.      * filter points to the program to exec.  Evaluate the regular
  665.      * expression against short_repository and exec the filter
  666.      * if it matches.
  667.      */
  668.     if (strcmp(exp, "DEFAULT") == 0) {
  669.         (void) strcpy(default_filter, filter);
  670.         continue;
  671.     }
  672.     /*
  673.      * For a regular expression of "ALL", send the log message
  674.      * to the requested filter *without* noting that a filter was run.
  675.      * This allows the "DEFAULT" regular expression to be more
  676.      * meaningful with all updates going to a master log file.
  677.      */
  678.     if (strcmp(exp, "ALL") == 0) {
  679.         (void) logfile_write(repository, filter, title, message);
  680.         continue;
  681.     }
  682.     if ((cp = re_comp(exp)) != NULL) {
  683.         warn(0, "bad regular expression at line %d file %s: %s",
  684.          line_number, logfile, cp);
  685.         continue;
  686.     }
  687.     if (re_exec(short_repository) == 0)
  688.         continue;            /* no match */
  689.     if (logfile_write(repository, filter, title, message) == 0)
  690.         filter_run = 1;
  691.     }
  692.     if (filter_run == 0 && default_filter[0] != '\0')
  693.     (void) logfile_write(repository, default_filter, title, message);
  694. }
  695.  
  696. /*
  697.  * Since some systems don't define this...
  698.  */
  699. #ifndef MAXHOSTNAMELEN
  700. #define    MAXHOSTNAMELEN    64
  701. #endif !MAXHOSTNAMELEN
  702.  
  703. /*
  704.  * Writes some stuff to the logfile "filter" and returns the status of the
  705.  * filter program.
  706.  */
  707. static
  708. logfile_write(repository, filter, title, message)
  709.     char *repository;
  710.     char *filter;
  711.     char *title;
  712.     char *message;
  713. {
  714.     char cwd[MAXPATHLEN], host[MAXHOSTNAMELEN];
  715.     FILE *fp;
  716.     char *cp;
  717.  
  718.     /*
  719.      * A maximum of 6 %s arguments are supported in the filter
  720.      */
  721.     (void) sprintf(prog, filter, title, title, title, title, title, title);
  722.     if ((fp = popen(prog, "w")) == NULL) {
  723.     warn(0, "cannot write entry to log filter: %s", prog);
  724.     return (1);
  725.     }
  726.     if (gethostname(host, sizeof(host)) < 0)
  727.     (void) strcpy(host, "(unknown)");
  728.     (void) fprintf(fp, "Update of %s\n", repository);
  729.     (void) fprintf(fp, "In directory %s:%s\n\n", host,
  730.            (cp = getwd(cwd)) ? cp : cwd);
  731.     setup_tmpfile(fp, "");
  732.     (void) fprintf(fp, "Log Message:\n%s\n", message);
  733.     return (pclose(fp));
  734. }
  735.  
  736. /*
  737.  * Called when "add"ing files to the RCS respository, as it is necessary
  738.  * to preserve the file modes in the same fashion that RCS does.  This would
  739.  * be automatic except that we are placing the RCS ,v file very far away from
  740.  * the user file, and I can't seem to convince RCS of the location of the
  741.  * user file.  So we munge it here, after the ,v file has been successfully
  742.  * initialized with "rcs -i".
  743.  */
  744. static
  745. fix_rcs_modes(rcs, user)
  746.     char *rcs;
  747.     char *user;
  748. {
  749.     struct stat sb;
  750.  
  751.     if (stat(user, &sb) != -1) {
  752.     (void) chmod(rcs, (int) sb.st_mode & ~0222);
  753.     }
  754. }
  755.  
  756. static
  757. commit_usage()
  758. {
  759.     (void) fprintf(stderr,
  760.     "%s %s [-fn] [-a] [-m 'message'] [-r revision] [files...]\n",
  761.            progname, command);
  762.     exit(1);
  763. }
  764.